using Godot;
using System;
using CurrentGame.Utils.Interpolation;


[Tool]
public partial class CameraShaderControl : ColorRect
{
	[ExportGroup("Darkness")]
	[Export]
	public float DarknessOffset
	{
		get => _darknessOffset;
		set
		{
			_darknessOffset = value;
			UpdateDarkness();
		}
	}
	private float _darknessOffset = 0.0f;

	[Export]
	public float DarknessScaler
    {
        get => _darknessScaler;
        set
        {
            _darknessScaler = value;
			_animated = true;
            UpdateDarkness();
        }
    }
	private float _darknessScaler = 1.0f, _currentScaler = 1.0f;

    [Export]
	public float DarknessBase
    {
        get => _darknessBase;
        set
        {
            _darknessBase = value;
			_animated = true;
            UpdateDarkness();
        }
    }
	private float _darknessBase = 1.0f, _currentBase = 1.0f;
	
	[Export]
	public float DarknessLimit
	{
		get => _darknessLimit;
		set
		{
			_darknessLimit = value;
			if (_shader != null) _shader.SetShaderParameter("limit", value);
		}
	}
	private float _darknessLimit = 1.0f;

	[ExportGroup("Color diffuse")]
    [Export]
	public float ColorDiffuseStrength
	{
		get => _colorDiffuseStrength;
		set
		{
			_colorDiffuseStrength = value;
			_animated = true;
			UpdateColorDiffuse();
		}
	}
	private float _colorDiffuseStrength = 0.2f, _currentDiffuse = 0.2f;

    [Export]
	public float ColorDiffuseOffset
    {
        get => _colorDiffuseOffset;
        set
        {
            _colorDiffuseOffset = value;
            UpdateColorDiffuse();
        }
    }
    private float _colorDiffuseOffset = 1.0f;


	[ExportGroup("Animation settings")]
	[Export]
	public float Factor = 1.0f;
	[Export]
	public float Interval = 0.001f;
	[Export]
	public float FlashInterpolationWeight = 0.3f;
	[Export]
	public float FadeInterpolationWeight = 0.1f;

	// Resource Ref
	private LerpInterpolation _interpolation;
    private ShaderMaterial _shader;
	private bool _animated = false;
	private double _currentTime = 0.0;
	private bool _flashing = false;


	public CameraShaderControl()
	{
		_interpolation = new(0.01f, new Vector3(-1, 0.000001f, -1))
		{
			Weight = 0.2f,
			Vector2Value = new Vector2(0.0f, 1.0f)
		};
		_interpolation.Teleport();
		_interpolation.ReachStableValue += Signal_InterpolationStabled;
	}

	// Called when the node enters the scene tree for the first time.
	public override void _Ready()
	{
		if (!Engine.IsEditorHint()) Visible = true;

		_shader = Material as ShaderMaterial;
		_currentBase = _darknessBase;
		_currentScaler = _darknessScaler;
		_currentDiffuse = _colorDiffuseStrength;
        _shader.SetShaderParameter("limit", _darknessLimit);
        UpdateColorDiffuse();
		UpdateDarkness();
	}


	private void FixedProcess()
	{
        if (_animated)
        {
            var stable = true;

            if (Mathf.Abs(_currentDiffuse - _colorDiffuseStrength) > 0.01f)
            {
                stable = false;
                _currentDiffuse = Mathf.Lerp(_currentDiffuse, _colorDiffuseStrength, Factor);
            }

            if (Mathf.Abs(_currentBase - _darknessBase) > 0.01f)
            {
                stable = false;
                _currentBase = Mathf.Lerp(_currentBase, _darknessBase, Factor);
            }

            if (Mathf.Abs(_currentScaler - _darknessScaler) > 0.01f)
            {
                stable = false;
                _currentScaler = Mathf.Lerp(_currentScaler, _darknessScaler, Factor);
            }


            if (stable)
            {
                _animated = false;
                _currentBase = _darknessBase;
                _currentScaler = _darknessScaler;
                _currentDiffuse = _colorDiffuseStrength;
            }

            UpdateDarkness();
            UpdateColorDiffuse();
        }
    }

    public override void _Process(double delta)
    {
		_currentTime += delta;
		if (_currentTime > Interval)
		{
			_currentTime = 0;
			FixedProcess();
		}
		if (_interpolation.Process(delta))
		{
			var current = _interpolation.Vector2Value;
			_shader.SetShaderParameter("Lighten", current.X);
			_shader.SetShaderParameter("Darken", current.Y);
		}
    }


	// private functions
	private void UpdateDarkness()
	{
		if (_shader != null)
		{
			_shader.SetShaderParameter("darkness",
				_currentBase + _darknessOffset * _currentScaler);
		}
	}

	private void UpdateColorDiffuse()
	{
		if (_shader != null)
		{
			_shader.SetShaderParameter("strength",
				_currentDiffuse * (1.0f + ColorDiffuseOffset));
		}
	}

	// Export interfaces
	public void FlashDarkenVisual(float param)
	{
        if (_flashing) return;
        _flashing = true;

		_interpolation.Weight = FlashInterpolationWeight;
		var current = _interpolation.Vector2Value;
		_interpolation.Vector2Value = new Vector2(current.X, param);
	}

	public void FlashLightenVisual(float param)
	{
		if (_flashing) return;
		_flashing = true;

		_interpolation.Weight = FlashInterpolationWeight;
        var current = _interpolation.Vector2Value;
        _interpolation.Vector2Value = new Vector2(param, current.Y);
    }

	// Signals - NOT GODOT
	private void Signal_InterpolationStabled(float v, Vector2 v2, Vector3 v3)
	{
		// We perform this callback only when it's flashing.
		if (_flashing)
		{
			_flashing = false;
			_interpolation.Weight = FadeInterpolationWeight;
			_interpolation.Vector2Value = new Vector2(0.0f, 1.0f);
		}
	}
}
